package com.xianggu.proxy;

import com.alibaba.fastjson2.JSON;
import com.xianggu.annotation.FeignClient;
import com.xianggu.fallback.FallbackFactory;
import com.xianggu.loadBalancing.LoadBalancer;
import com.xianggu.loadBalancing.RandomLoadBalancingStrategy;
import com.xianggu.protocol.clint.HttpClint;
import com.xianggu.protocol.Invocation;
import com.xianggu.protocol.URL;
import com.xianggu.register.RedisLocalRegister;

import java.beans.Introspector;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.List;

/**
 * @author chengjy
 * @date 2025/3/8
 */
public class InterfaceRpcProxy {

    public static <T> T getProxy(Class<?> clazz) {

        int max = 3;
        Object o = Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{clazz}, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                if ("toString".equals(method.getName())) {
                    return clazz.getName();
                }

                Class<?> returnType = method.getReturnType();
                // bean名称
                String beanName = Introspector.decapitalize(clazz.getSimpleName());
                if (clazz.isAnnotationPresent(FeignClient.class)) {
                    FeignClient feignClient = clazz.getAnnotation(FeignClient.class);
                    if (feignClient.beanName() != null && !feignClient.beanName().isEmpty()) {
                        beanName = feignClient.beanName();
                    }
                }

                Invocation invocation = new Invocation(beanName, method.getName()
                        , method.getParameterTypes(), args, clazz , null);
                // 服务发现
                // 获取服务名
                String serviceName = "";
                Class<?> fallbackClass = null;
                RuntimeException err = null;

                if (clazz.isAnnotationPresent(FeignClient.class)) {
                    FeignClient feignClient = clazz.getAnnotation(FeignClient.class);
                    serviceName = feignClient.serviceName();
                    // 查看有无服务容错实现
                    fallbackClass = feignClient.fallbackFactory();
                }
                // 获取服务端口
                List<URL> register = RedisLocalRegister.getRegister(serviceName);
//                List<URL> register = LocalServeRegister.getRegister(serviceName);
                // 负载均衡
                LoadBalancer loadBalancer = new LoadBalancer(new RandomLoadBalancingStrategy());
                URL url = loadBalancer.selectServer(register);
                // 服务调用

                // 服务失败重试
                for (int i = 0; i < max; i++) {
                    try {
                        System.out.println(method.getName() + "调用" + serviceName + "服务：" + url.getHostName() + ":" + url.getProt());
                        String resultJson = HttpClint.sent(invocation, url.getHostName(), url.getProt());
                        return JSON.parseObject(resultJson, returnType);
                    } catch (RuntimeException e) {
                        Thread.sleep(3000);
                        System.out.println("调用失败，重试第" + (i + 1) + "次");
                        if (register.size() > 1) {
                            // 重新负载均衡
                            register.remove(url);
                            url = loadBalancer.selectServer(register);
                        }
                        err = e;
                    }
                }

                // 服务降级
                if (fallbackClass != null) {
                    System.out.println("正在走服务降级逻辑！");
                    if (FallbackFactory.class.isAssignableFrom(fallbackClass)) {
                        // 如果是fallback实例，则执行对应的 create方法
                        return ((FallbackFactory) fallbackClass.newInstance()).create(err);
                    } else {
                        // 如果是工厂，则反射调用对应的方法（通过方法名+参数即可找到对应的method）
                        return fallbackClass.newInstance().getClass()
                                .getMethod(method.getName(), method.getParameterTypes()).invoke(fallbackClass.newInstance(), args);
                    }
                }
                throw new RuntimeException("服务调用失败，请检查网络");
            }
        });
        return (T) o;
    }
}
